// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#include "rotor-light/actor.hpp"
#include "rotor-light/planner.hpp"
#include "rotor-light/queue.hpp"
#include "rotor-light/supervisor.hpp"
#include <cstdio>
#include <new>

namespace rotor_light {

ActorBase::ActorBase(char *backends, size_t backends_count_)
    : id{0}, backends_ptr{backends}, backends_count{backends_count_},
      backend_idx{-1}, fail_policy{FailPolicy::escalate} {}

void ActorBase::initialize() { subscribe(&ActorBase::on_state_change); }

void ActorBase::on_state_change(message::ChangeState &message) {
  assert(message.to == id);
  auto cmd = message.command;
  if (cmd == StateCommand::init) {
    assert(state == State::off);
    state = State::initializing;
    advance_init();
  } else if (cmd == StateCommand::start) {
    assert(state == State::initialized);
    state = State::operational;
    advance_start();
  } else if (cmd == StateCommand::shutdown) {
    assert((state != State::off) && (state != State::shutting_down));
    state = State::shutting_down;
    advance_stop();
  }
}

void ActorBase::advance_init() {
  state = State::initialized;
  send<message::ChangeStateAck>(0, supervisor->id, id, State::initialized,
                                true);
}

void ActorBase::advance_start() {}

void ActorBase::advance_stop() {
  state = State::off;
  send<message::ChangeStateAck>(0, supervisor->id, id, State::off, true);
  backend_idx = -1;
}

uint8_t ActorBase::bind(ActorId initial_value, SupervisorBase *supervisor,
                        Context &) {
  assert(initial_value);
  this->supervisor = supervisor;
  id = mask = initial_value;
  return 1;
}

void ActorBase::stop() {
  if (state != State::off && state != State::shutting_down) {
    send<message::ChangeState>(0, id, StateCommand::shutdown);
  }
}

EventId ActorBase::add_event(Duration delta, Callback callback, void *data) {
  assert(supervisor->now && supervisor->planner);
  auto when = supervisor->now() + delta;
  return supervisor->planner->add_event(when, callback, data);
}

void ActorBase::cancel_event(EventId event_id) {
  supervisor->planner->remove_event(event_id);
}

} // namespace rotor_light
